#region Using Statements
using System;
using System.IO;
using System.Xml;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Runtime.Remoting;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace SkyBurner
{
    #region
    public class BaseWorldLight
    {
        protected Vector3 m_vLightPosition;
        public Vector3 Position
        {
            get { return m_vLightPosition; }
        }

        protected Vector3 m_vLightDirection;
        protected Color m_lightColor;           // TODO define light color(s)

        public BaseWorldLight(Vector3 pPosition, Vector3 pDirection)
        {
            m_vLightPosition = pPosition;
            m_vLightDirection = pDirection;
        }
    }
    #endregion

    #region BaseWorld class
    public abstract class BaseWorld : BaseObject
    {
        
        protected Camera m_Camera;        
        protected Collection<BaseDrawableObject> m_drawableObjects = new Collection<BaseDrawableObject>();
        protected SortedList<String, BaseWorldLight> m_lightSources = new SortedList<String, BaseWorldLight>();
        protected ParticleSystemManager m_particleSystemManager;
        protected DynamicCodeManager m_dynamicCodeManager;

        public ParticleSystemManager ParticleSystemManager
        {
            get { return m_particleSystemManager; }
        }

        public DynamicCodeManager DynamicCodeManager
        {
            get { return m_dynamicCodeManager; }
        }

        public BaseWorld(BaseGame pGame)
            : base(pGame)
        {
            // create world camera...
            m_Camera = new Camera(m_Game);
            m_Camera.Position = new Vector3(0.0f, 0.0f, 0.0f);
            m_Camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
            m_Camera.View = new Vector3(0.0f, 0.0f, 1.0f);
            m_Camera.NearPlane = 0.1f;
            m_Camera.FarPlane = 2000.0f;
            m_Camera.FOV = MathHelper.PiOver2;
            m_Camera.AspectRatio = (float)m_Game.GraphicsDevice.Viewport.Width / m_Game.GraphicsDevice.Viewport.Height;

            // create dynamic code manager...
            m_dynamicCodeManager = new DynamicCodeManager(m_Game);

            // load particle emitters...
            m_particleSystemManager = new ParticleSystemManager(m_Game);
            m_particleSystemManager.Load("Data\\Config\\ParticleEmitters.xml");
        }

        public void AddLight(String pName, BaseWorldLight pBaseWorldLight)
        {
            m_lightSources.Add(pName, pBaseWorldLight);
        }

        public BaseWorldLight GetLight(String pName)
        {
            int i = m_lightSources.IndexOfKey(pName);
            if (i >= 0)
                return m_lightSources.Values[i];

            return null;
        }

        public void RemoveLight(String pName)
        {
            int i = m_lightSources.IndexOfKey(pName);
            if (i >= 0)
                m_lightSources.RemoveAt(i);
            else
                throw new Exception(String.Format("BaseWorld.RemoveLigh: Cannot find light with name [{0}]", pName));
        }

        public void Update(GameTime gameTime)
        {
            // call world Update...
            OnUpdate(gameTime);

            // update dynamic code...
            m_dynamicCodeManager.Update(gameTime, m_Camera);

            // update particle system...
            m_particleSystemManager.Update(gameTime, m_Camera);
        }

        public void Draw(GameTime gameTime)
        {
            // call world draw...
            OnDraw(gameTime);

            // draw dynamic code...
            m_dynamicCodeManager.Draw(gameTime, m_Camera);

            // render particle at last...
            m_particleSystemManager.Draw(gameTime, m_Camera);
        }

        protected abstract void OnUpdate(GameTime gameTime);
        protected abstract void OnDraw(GameTime gameTime);
    }
    #endregion
}
